using System;
using System.Linq;
using RomanJendrusz.Amazon.ETL.Domain;
using RomanJendrusz.Amazon.ETL.Infrastructure.Bus;
using RomanJendrusz.Amazon.ETL.Messages;
using RomanJendrusz.Amazon.ETL.Exceptions;
using System.Collections.Generic;
using RomanJendrusz.Amazon.ETL.Infrastructure.Loging.Core;
using RomanJendrusz.Amazon.ETL.Infrastructure;

namespace RomanJendrusz.Amazon.ETL.Services
{
    public class BookService: IBookService
    {
        private readonly IBus bus;

        public BookService(IBus bus)
        {
            this.bus = bus;
        }

        public Book GetBookDetailsByAsin(string asin)
        {
            if (asin == null) throw new ArgumentNullException("asin");
            var request = new GetBookDetailsRequest {Identifier = asin, IdentifierType = BookIdentifierType.ASIN};
            var response = bus.Process(request);
            if(response.IsValid == false)
            {
                throw new InvalidBookException("The book with asin " + asin + " is invalid");
            }
            return response.As<GetBookDetailsResponse>().RetrievedBook;
        }

        public Book GetBookDetailsByIsbn(string isbn)
        {
            if (isbn == null) throw new ArgumentNullException("isbn");
            var request = new GetBookDetailsRequest { Identifier = isbn, IdentifierType = BookIdentifierType.ISBN };
            var response = bus.Process(request);
            if (response.IsValid == false)
            {
                throw new InvalidBookException("The book with asin " + isbn + " is invalid");
            }
            return response.As<GetBookDetailsResponse>().RetrievedBook;
        }

        public ICollection<CustomerReview> GetBookReviewsFor(Book book)
        {
            if (book == null) throw new ArgumentNullException("book");
            if (string.IsNullOrEmpty(book.Asin))
            {
                throw new InvalidOperationException("Can't search for a book with blank asin");
            }
            var countRequest = new GetBookReviewsCountRequest{BookAsin= book.Asin};
            var countResponse = bus.Process(countRequest).As<GetItemsCountResponse>();
            var reviewsRequests = new List<GetBookReviewsRequest>();
            for (int i = 1; i <= countResponse.PageCount; i++)
            {
                reviewsRequests.Add(new GetBookReviewsRequest{BookAsin=book.Asin, PageNumber = i});
            }
            var reviewsResponses = bus.Process(reviewsRequests.ToArray());
            var customerReviews = new List<CustomerReview>();
            foreach (var response in reviewsResponses)
            {
                foreach (var review in response.As<GetBookReviewsResponse>().Reviews)
                {
                    review.Book = book;
                    customerReviews.Add(review);
                }
            }
            removeDuplicatedCustomers(customerReviews);
            return customerReviews;
        }

        private void removeDuplicatedCustomers(IList<CustomerReview> customerReviews)
        {
            IEnumerable<IGrouping<string, CustomerReview>> groupBy = customerReviews.Where(r => r.Customer != null)
                .GroupBy(r => r.Customer.CustomerId).Where(g => g.Count() > 1);
            foreach (var reviews in groupBy)
            {
                List<CustomerReview> list = reviews.ToList();
                Customer customer = list[0].Customer;
                if (customer != null)
                {
                    list.ForEach(r => r.Customer = customer);
                }
            }
        }

        public ICollection<Book> GetSimiliarBooksFor(Book book)
        {
            if (book == null) throw new ArgumentNullException("book");
            if(string.IsNullOrEmpty(book.Asin))
            {
                throw new InvalidOperationException("Can't search for a book with blank asin");
            }
            var request = new GetSimiliarBooksRequest {BookAsin = book.Asin};
            var response = bus.Process(request);
            return response.As<GetSimiliarBooksResponse>().SimiliarBooks;
        }

        public IList<BrowseNode> GetBrowseNodesFor(Book book)
        {
            AssertThat.IsNotNull(book, "book");
            if (string.IsNullOrEmpty(book.Asin))
            {
                throw new InvalidOperationException("Can't search for a book with blank asin");
            }
            var request = new GetBrowseNodesForProductRequest { Asin = book.Asin };
            var response = bus.Process(request);
            var nodes = response.As<GetBrowseNodesForProductResponse>().BrowseNodes;
            foreach(var node in nodes)
            {
                node.AddBook(book);
            }
            return nodes;
        }


        public ICollection<ListMania> GetListmaniasFor(Book book)
        {
            throw new NotImplementedException();
        }

        
    }
}